Micron Document
Aleph Git

Commit 337d8771de8876e6ece9ab7ef882b6410ad7a0d0


Parents : 2f74daa
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-30T20:55:33-05:00

Refactor HTTPTunnelInterface configuration and initialization; clarify mode options and improve error handling

Changes

1 files changed, 96 insertions(+), 31 deletions(-)

M HTTPInterface.py +96 -31

Diff

diff --git a/HTTPInterface.py b/HTTPInterface.py
index 754d73d..6cd0df8 100755
--- a/HTTPInterface.py
+++ b/HTTPInterface.py
@@ -4,7 +4,6 @@ import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from queue import Empty, Queue
from socketserver import ThreadingMixIn
-from threading import Event, Thread
import requests
import RNS
@@ -18,7 +17,7 @@ class HTTPTunnelInterface(Interface):
allowing Reticulum to traverse firewalls and proxies that allow HTTP/HTTPS traffic.
Configuration:
- mode: "client" or "server"
+ mode: "client" or "server" (tunnel role; distinct from Reticulum interface_mode keywords)
listen_host: IP address to bind server to (server mode)
listen_port: Port to bind server to (server mode)
server_url: URL of the HTTP server (client mode)
@@ -27,9 +26,11 @@ class HTTPTunnelInterface(Interface):
serve_html_page: Serve HTML page on GET requests (default: False)
html_file_path: Path to HTML file to serve (optional)
+ The config "type" must match the interface module basename (HTTPInterface.py -> type = HTTPInterface).
+
Config example for server:
[[HTTP Tunnel Server]]
- type = HTTPTunnelInterface
+ type = HTTPInterface
interface_enabled = True
mode = server
listen_host = 0.0.0.0
@@ -37,7 +38,7 @@ class HTTPTunnelInterface(Interface):
Config example for server with HTML:
[[HTTP Tunnel Server]]
- type = HTTPTunnelInterface
+ type = HTTPInterface
interface_enabled = True
mode = server
listen_host = 0.0.0.0
@@ -47,7 +48,7 @@ class HTTPTunnelInterface(Interface):
Config example for client:
[[HTTP Tunnel Client]]
- type = HTTPTunnelInterface
+ type = HTTPInterface
interface_enabled = True
mode = client
server_url = http://example.com:8080/
@@ -69,23 +70,36 @@ class HTTPTunnelInterface(Interface):
self.name = ifconf["name"]
- mode = ifconf["mode"] if "mode" in ifconf else "client"
+ mode = str(ifconf["mode"]).lower() if "mode" in ifconf else "client"
listen_host = ifconf["listen_host"] if "listen_host" in ifconf else "0.0.0.0"
listen_port = int(ifconf["listen_port"]) if "listen_port" in ifconf else 8080
server_url = ifconf["server_url"] if "server_url" in ifconf else None
- poll_interval = float(ifconf["poll_interval"]) if "poll_interval" in ifconf else self.DEFAULT_POLL_INTERVAL
- check_user_agent = ifconf.as_bool("check_user_agent") if "check_user_agent" in ifconf else True
+ poll_interval = (
+ float(ifconf["poll_interval"])
+ if "poll_interval" in ifconf
+ else self.DEFAULT_POLL_INTERVAL
+ )
+ check_user_agent = (
+ ifconf.as_bool("check_user_agent") if "check_user_agent" in ifconf else True
+ )
mtu = int(ifconf["mtu"]) if "mtu" in ifconf else self.DEFAULT_MTU
- serve_html_page = ifconf.as_bool("serve_html_page") if "serve_html_page" in ifconf else False
- html_file_path = ifconf["html_file_path"] if "html_file_path" in ifconf else None
+ serve_html_page = (
+ ifconf.as_bool("serve_html_page") if "serve_html_page" in ifconf else False
+ )
+ html_file_path = (
+ ifconf["html_file_path"] if "html_file_path" in ifconf else None
+ )
if mode not in ["client", "server"]:
- raise ValueError(f"Invalid mode '{mode}' for {self}. Must be 'client' or 'server'")
+ raise ValueError(
+ f"Invalid mode '{mode}' for {self}. Must be 'client' or 'server'",
+ )
if mode == "client" and server_url is None:
raise ValueError(f"No server_url specified for client mode in {self}")
self.owner = owner
+ self.IN = True
self.mode = mode
self.mtu = mtu
self.check_user_agent = check_user_agent
@@ -98,7 +112,7 @@ class HTTPTunnelInterface(Interface):
self._recv_queue = Queue()
self._send_queue = Queue()
- self._stop_event = Event()
+ self._stop_event = threading.Event()
self.HW_MTU = mtu
self.online = False
@@ -128,7 +142,9 @@ class HTTPTunnelInterface(Interface):
RNS.log(f"HTML file not found: {self.html_file_path}", RNS.LOG_WARNING)
self.html_content = None
except Exception as e:
- RNS.log(f"Error loading HTML file {self.html_file_path}: {e}", RNS.LOG_ERROR)
+ RNS.log(
+ f"Error loading HTML file {self.html_file_path}: {e}", RNS.LOG_ERROR,
+ )
self.html_content = None
def setup_server(self):
@@ -136,10 +152,16 @@ class HTTPTunnelInterface(Interface):
class TunnelRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
- if self.path == "/" and interface_instance.serve_html_page and interface_instance.html_content:
+ if (
+ self.path == "/"
+ and interface_instance.serve_html_page
+ and interface_instance.html_content
+ ):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
- self.send_header("Content-Length", str(len(interface_instance.html_content)))
+ self.send_header(
+ "Content-Length", str(len(interface_instance.html_content)),
+ )
self.end_headers()
self.wfile.write(interface_instance.html_content.encode("utf-8"))
else:
@@ -151,7 +173,10 @@ class HTTPTunnelInterface(Interface):
if interface_instance.check_user_agent:
user_agent = self.headers.get("User-Agent", "")
if user_agent != HTTPTunnelInterface.TUNNEL_USER_AGENT:
- RNS.log(f"Rejected request with invalid User-Agent: {user_agent}", RNS.LOG_WARNING)
+ RNS.log(
+ f"Rejected request with invalid User-Agent: {user_agent}",
+ RNS.LOG_WARNING,
+ )
self.send_response(403)
self.send_header("Content-Type", "text/plain")
self.end_headers()
@@ -159,22 +184,32 @@ class HTTPTunnelInterface(Interface):
return
content_length = int(self.headers.get("Content-Length", 0))
- client_data = self.rfile.read(content_length) if content_length > 0 else b""
+ client_data = (
+ self.rfile.read(content_length) if content_length > 0 else b""
+ )
if client_data:
- RNS.log(f"Received {len(client_data)} bytes from client", RNS.LOG_EXTREME)
+ RNS.log(
+ f"Received {len(client_data)} bytes from client",
+ RNS.LOG_EXTREME,
+ )
interface_instance._recv_queue.put(client_data)
server_data_parts = []
while not interface_instance._send_queue.empty():
try:
- server_data_parts.append(interface_instance._send_queue.get_nowait())
+ server_data_parts.append(
+ interface_instance._send_queue.get_nowait(),
+ )
except Empty:
break
server_data = b"".join(server_data_parts)
if server_data:
- RNS.log(f"Sending {len(server_data)} bytes ({len(server_data_parts)} chunks) to client", RNS.LOG_EXTREME)
+ RNS.log(
+ f"Sending {len(server_data)} bytes ({len(server_data_parts)} chunks) to client",
+ RNS.LOG_EXTREME,
+ )
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
@@ -193,7 +228,9 @@ class HTTPTunnelInterface(Interface):
def run_server():
try:
- self._http_server = ThreadedHTTPServer((self.listen_host, self.listen_port), TunnelRequestHandler)
+ self._http_server = ThreadedHTTPServer(
+ (self.listen_host, self.listen_port), TunnelRequestHandler,
+ )
self._http_server.serve_forever()
except Exception as e:
if not self._stop_event.is_set():
@@ -201,7 +238,7 @@ class HTTPTunnelInterface(Interface):
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
- self._server_thread = Thread(target=run_server, daemon=True)
+ self._server_thread = threading.Thread(target=run_server, daemon=True)
self._server_thread.start()
thread = threading.Thread(target=self.receive_loop)
@@ -209,11 +246,16 @@ class HTTPTunnelInterface(Interface):
thread.start()
self.online = True
- RNS.log(f"HTTP server started on http://{self.listen_host}:{self.listen_port}", RNS.LOG_NOTICE)
+ RNS.log(
+ f"HTTP server started on http://{self.listen_host}:{self.listen_port}",
+ RNS.LOG_NOTICE,
+ )
def setup_client(self):
self.session = requests.Session()
- self.session.headers.update({"User-Agent": HTTPTunnelInterface.TUNNEL_USER_AGENT})
+ self.session.headers.update(
+ {"User-Agent": HTTPTunnelInterface.TUNNEL_USER_AGENT},
+ )
self._consecutive_failures = 0
self._max_backoff = 30.0
@@ -247,11 +289,16 @@ class HTTPTunnelInterface(Interface):
try:
RNS.log(f"Sending {len(data_to_send)} bytes to server", RNS.LOG_EXTREME)
- response = self.session.post(self.server_url, data=data_to_send, timeout=5)
+ response = self.session.post(
+ self.server_url, data=data_to_send, timeout=5,
+ )
response.raise_for_status()
if response.content:
- RNS.log(f"Received {len(response.content)} bytes from server", RNS.LOG_EXTREME)
+ RNS.log(
+ f"Received {len(response.content)} bytes from server",
+ RNS.LOG_EXTREME,
+ )
self.process_incoming(response.content)
if self._consecutive_failures > 0:
@@ -261,10 +308,16 @@ class HTTPTunnelInterface(Interface):
except requests.exceptions.RequestException as e:
self._consecutive_failures += 1
if self._consecutive_failures % 10 == 1:
- RNS.log(f"Error communicating with server for {self} (attempt {self._consecutive_failures}): {e}", RNS.LOG_WARNING)
+ RNS.log(
+ f"Error communicating with server for {self} (attempt {self._consecutive_failures}): {e}",
+ RNS.LOG_WARNING,
+ )
if self._consecutive_failures > 0:
- delay = min(self.poll_interval * (2 ** min(self._consecutive_failures - 1, 5)), self._max_backoff)
+ delay = min(
+ self.poll_interval * (2 ** min(self._consecutive_failures - 1, 5)),
+ self._max_backoff,
+ )
else:
delay = self.poll_interval
@@ -278,7 +331,10 @@ class HTTPTunnelInterface(Interface):
def process_outgoing(self, data):
if self.online:
if len(data) > self.mtu:
- RNS.log(f"Payload too large ({len(data)} > {self.mtu}) for {self}", RNS.LOG_ERROR)
+ RNS.log(
+ f"Payload too large ({len(data)} > {self.mtu}) for {self}",
+ RNS.LOG_ERROR,
+ )
return
self._send_queue.put(data)
@@ -289,13 +345,22 @@ class HTTPTunnelInterface(Interface):
self._stop_event.set()
self.online = False
+ if self.mode == "client" and getattr(self, "session", None) is not None:
+ try:
+ self.session.close()
+ except Exception as e:
+ RNS.log(f"Error closing HTTP session for {self}: {e}", RNS.LOG_DEBUG)
+
if self.mode == "server":
if hasattr(self, "_http_server") and self._http_server:
try:
self._http_server.shutdown()
self._http_server.server_close()
except Exception as e:
- RNS.log(f"Error while shutting down HTTP server for {self}: {e}", RNS.LOG_ERROR)
+ RNS.log(
+ f"Error while shutting down HTTP server for {self}: {e}",
+ RNS.LOG_ERROR,
+ )
if hasattr(self, "_server_thread") and self._server_thread:
self._server_thread.join(timeout=2)
@@ -308,5 +373,5 @@ class HTTPTunnelInterface(Interface):
return f"HTTPTunnelInterface[{self.name}/server/{self.listen_host}:{self.listen_port}]"
return f"HTTPTunnelInterface[{self.name}/client/{self.server_url}]"
-interface_class = HTTPTunnelInterface
+interface_class = HTTPTunnelInterface

Served by rngit 1.5.0 - Generated in 0.01s